package view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import dao.FieldDao;

import model.Endpoint;
import model.EndpointType;
import model.Field;
import model.Tile;
import model.TileType;

public class GameCanvas extends JPanel {
	private Field field;

	private Stroke border = new BasicStroke(3);
	private Stroke connections = new BasicStroke(1.5f);

	private Point clicked = null;
	private Point mouse = null;

	private double drawwidth, drawheight;
	private double xborder, yborder;
	private double scale;

	private Image background;
	private double backgroundscale = 100.0;
	private int backgroundxborder = 210;
	private int backgroundyborder = 274;
	private Image leed;
	private Image leed5;
	private Image leed10;
	private Image leed20;
	private Image leed22;

	public GameCanvas() {
		try {
			this.background = ImageIO.read(new File(
					"resources/images/background.png"));
			this.leed = ImageIO.read(new File("resources/images/off.png"));
			this.leed5 = ImageIO.read(new File("resources/images/blue.png"));
			this.leed10 = ImageIO.read(new File("resources/images/red.png"));
			this.leed20 = ImageIO.read(new File("resources/images/green.png"));
			this.leed22 = ImageIO.read(new File("resources/images/yellow.png"));
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		// Tile[][] tiles = new Tile[10][5];
		// TileType type = new TileType(new int[][] { { 1, 4, 7 } }, "test");
		// for (int x = 0; x < tiles.length; x++) {
		// for (int y = 0; y < tiles[x].length; y++) {
		// tiles[x][y] = new Tile();
		// tiles[x][y].setType(type);
		// }
		// }
		// field = new Field();
		// field.setTiles(tiles);
		// Endpoint[] endpoints = new Endpoint[8];
		// for(int i = 0; i < endpoints.length; i++) {
		// EndpointType etype = (i % 2 == 0 ? EndpointType.INPUT :
		// EndpointType.OUTPUT);
		// int voltageId = i / 2;
		// int positionId = i * 4;
		// endpoints[i] = new Endpoint(etype, voltageId, positionId);
		//
		// }
		// field.setEndpoints(endpoints);

		FieldDao dao = new FieldDao();
		try {
			this.field = dao.load("sample");
			this.setPreferredSize(new Dimension(
					this.field.getTiles().length * 100,
					this.field.getTiles()[0].length * 100));
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		this.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseReleased(MouseEvent arg0) {
				Point released = getTileByPoint(arg0.getPoint());
				if (released != null
						&& released.equals(getTileByPoint(clicked))
						&& !field.isSolved()) {
					field.getTiles()[released.x][released.y].rotate();
					field.checkForSolved();
					GameCanvas.this.repaint();
					if (field.isSolved()) {
						JOptionPane.showMessageDialog(GameCanvas.this,
								"Resolved!", "Resolved",
								JOptionPane.INFORMATION_MESSAGE);
					}
				}
				GameCanvas.this.clicked = null;
				GameCanvas.this.repaint();
			}

			@Override
			public void mousePressed(MouseEvent arg0) {
				GameCanvas.this.clicked = arg0.getPoint();
				Point index = GameCanvas.this.getTileByPoint(arg0.getPoint());
				Tile tile = field.getTiles()[index.x][index.y];
				GameCanvas.this.repaint();
			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				GameCanvas.this.mouse = null;
				GameCanvas.this.repaint();
			}

			@Override
			public void mouseEntered(MouseEvent arg0) {
				GameCanvas.this.mouse = arg0.getPoint();
				GameCanvas.this.repaint();
			}
		});
		this.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				super.mouseMoved(e);
				GameCanvas.this.mouse = e.getPoint();
				GameCanvas.this.repaint();
			}

			@Override
			public void mouseDragged(MouseEvent e) {
				super.mouseDragged(e);
				GameCanvas.this.mouse = e.getPoint();
				GameCanvas.this.repaint();
			}
		});

	}

	private Point getTileByPoint(Point p) {
		double x = (p.x - xborder) / scale;
		double y = (p.y - yborder) / scale;
		Point tile = new Point((int) (x), (int) (y));
		if (x >= 0 && x < this.field.getTiles().length && y >= 0
				&& y < this.field.getTiles()[tile.x].length) {
			return tile;
		} else {
			return null;
		}
	}

	private void calculateValues() {
		Tile[][] tiles = field.getTiles();
		if (1.0 * (tiles.length + 1) / (tiles[0].length + 1) > 1.0
				* this.getSize().width / this.getSize().height) {
			drawwidth = 1.0 * this.getSize().width / (tiles.length + 1)
					* tiles.length;
			drawheight = drawwidth / tiles.length * tiles[0].length;
		} else {
			drawheight = 1.0 * this.getSize().height / (tiles[0].length + 1)
					* tiles[0].length;
			drawwidth = drawheight / tiles[0].length * tiles.length;
			yborder = 0;
			xborder = (this.getSize().width - drawwidth) / 2;
		}
		xborder = (this.getSize().width - drawwidth) / 2;
		yborder = (this.getSize().height - drawheight) / 2;
		// scale = drawwidth / tiles.length;
		scale = 100;

		border = new BasicStroke((float) (scale / 30));
		connections = new BasicStroke((float) (scale / 30));
	}

	private double[] getEndpointByIndex(int index) {
		double[] position = new double[2];
		Tile[][] tiles = this.field.getTiles();
		int count = ((tiles.length - 1) * 2 + (tiles[0].length - 1) * 2 + 4);
		if (index < tiles[0].length) {
			position[0] = tiles.length + 0.25;
			position[1] = index + 0.5;
		} else if (index < tiles[0].length + tiles.length) {
			int i = (tiles[0].length + tiles.length) - index;
			position[0] = i - 0.5;
			position[1] = tiles[0].length + 0.25;
		} else if (index < tiles[0].length * 2 + tiles.length) {
			int i = (tiles[0].length * 2 + tiles.length) - index;
			position[0] = -0.25;
			position[1] = i - 0.5;
		} else if (index < tiles[0].length * 2 + tiles.length * 2) {
			int i = index - (tiles[0].length * 2 + tiles.length);
			position[0] = i + 0.5;
			position[1] = -0.25;
		}
		return position;
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2 = (Graphics2D) g;
		GraphicsOptimizer.optimizeOptions(g2);
		if (this.field != null) {

			g2.drawImage(
					this.background,
					(int) (xborder - backgroundxborder / backgroundscale
							* scale),
					(int) (yborder - backgroundyborder / backgroundscale
							* scale),
					(int) (this.background.getWidth(null) / backgroundscale * scale),
					(int) (this.background.getHeight(null) / backgroundscale * scale),
					null);
			// if(!field.isSolved()) {
			calculateValues();
			Tile[][] tiles = field.getTiles();
			for (int x = 0; x < tiles.length; x++) {
				for (int y = 0; y < tiles[x].length; y++) {
					if (tiles[x][y] != null) {
						g2.setStroke(border);
						g2.setColor(Color.GREEN);
						// g2.drawRect((int) (x * scale + xborder), (int) (y
						// * scale + yborder), (int) scale, (int) scale);
						double xtrans = x * scale + xborder + scale / 2;
						double ytrans = y * scale + yborder + scale / 2;
						g2.translate(xtrans, ytrans);
						double rotation = 0;
						switch (tiles[x][y].getRotation()) {
						case DEGREES_0:
							rotation -= Math.PI / 2;
						case DEGREES_90:
							rotation -= Math.PI / 2;
						case DEGREES_180:
							rotation -= Math.PI / 2;
						case DEGREES_270:
							rotation -= Math.PI / 2;
						}
						g2.rotate(rotation);
						g2.drawImage(tiles[x][y].getType().getImage(),
								(int) (-scale / 2), (int) (-scale / 2),
								(int) scale, (int) scale, null);
						g2.rotate(-rotation);
						// g2.rotate(0);
						g2.translate(-xtrans, -ytrans);
						int[][] connections = tiles[x][y].getConnections();
						g2.setStroke(this.connections);
						g2.setColor(Color.GRAY);
						for (int[] connection : connections) {
							for (int point : connection) {
								double[] pointcoord = endpointIndexToCoord(point);
								double x1 = pointcoord[0] * scale + x * scale
										+ xborder;
								double y1 = pointcoord[1] * scale + y * scale
										+ yborder;
								for (int other : connection) {
									if (point != other) {
										double[] othercoord = endpointIndexToCoord(other);
										double x2 = othercoord[0] * scale + x
												* scale + xborder;
										double y2 = othercoord[1] * scale + y
												* scale + yborder;
										// g2.drawLine((int) x1, (int) y1,
										// (int) x2, (int) y2);
									}
								}
							}
						}
					}
				}
			}
			// if (this.mouse != null) {
			// g2.drawOval(mouse.x - 5, mouse.y - 5, 10, 10);
			// if (this.clicked != null) {
			// g2.drawOval(clicked.x - 5, clicked.y - 5, 10, 10);
			// }
			// Point mp = getTileByPoint(mouse);
			// if (mp != null) {
			// g2.drawRect((int) (mp.x * scale + xborder), (int) (mp.y
			// * scale + yborder), (int) scale, (int) scale);
			// }
			// if (clicked != null) {
			// Point clp = getTileByPoint(clicked);
			// if (clp != null) {
			// g2.drawRect((int) (mp.x * scale + xborder), (int) (mp.y
			// * scale + yborder), (int) scale, (int) scale);
			// }2
			// }
			//
			// }
			for (Endpoint endpoint : this.field.getEndpoints()) {
				double[] position = getEndpointByIndex(endpoint.getPositionId());
				double x = (position[0] - 0.125) * scale + xborder;
				double y = (position[1] - 0.125) * scale + yborder;
				Image leed = this.leed;
				boolean active = false;
				int[] voltages = this.field.getConnectedVoltages();
				for (int volt : voltages) {
					if (volt == endpoint.getVoltageId()) {
						active = true;
						break;
					}
				}
				if (active) {
					switch (endpoint.getVoltageId()) {
					case 5:
						leed = this.leed5;
						break;
					case 10:
						leed = this.leed10;
						break;
					case 20:
						leed = this.leed20;
						break;
					case 22:
						leed = this.leed22;
						break;
					}
				}
				g2.drawImage(leed, (int) x, (int) y, (int) (scale / 4),
						(int) (scale / 4), null);
				// g2.drawOval((int) (x), (int) (y), (int) (scale / 2),
				// (int) (scale / 2));
				// g2.drawString("" + endpoint.getVoltageId(), (float) (x +
				// scale / 4),
				// (float) (y + scale / 4));
			}
			// } else {
			// g2.drawString("Solved!", 20, 20);
			// }
		}
	}

	private double[] endpointIndexToCoord(int index) {
		switch (index) {
		case 0:
			return new double[] { 1, 0.25 };
		case 1:
			return new double[] { 1, 0.5 };
		case 2:
			return new double[] { 1, 0.75 };
		case 3:
			return new double[] { 0.75, 1 };
		case 4:
			return new double[] { 0.5, 1 };
		case 5:
			return new double[] { 0.25, 1 };
		case 6:
			return new double[] { 0, 0.75 };
		case 7:
			return new double[] { 0, 0.5 };
		case 8:
			return new double[] { 0, 0.25 };
		case 9:
			return new double[] { 0.25, 0 };
		case 10:
			return new double[] { 0.5, 0 };
		case 11:
			return new double[] { 0.75, 0 };
		}
		return null;
	}
}
